#ifndef DELEGATE_H
#define DELEGATE_H
#include "StompedReality/Core.h"
#include <functional>
namespace StompedReality
{

/*!
 * \author	Stefan Kazalski
 *
 * \brief	Delegate with parameters.
 *
 *   Can be created by StompedReality::CreateDelegate(obj,&OBJECT::FUNCTION)
 */
template <class OBJECT_TYPE, typename INPUT_TYPE, typename RETURN_TYPE>
class DLL_EXPORT DelegateInOut
{
 private:

  std::function<RETURN_TYPE ( OBJECT_TYPE*, INPUT_TYPE )> m_Function;
  OBJECT_TYPE* m_ObjectPTR;

 public:

  //! \brief	Constructor.
  //! \param	obj		The Obejct.
  //! \param	fn
  DelegateInOut ( OBJECT_TYPE* obj, std::function<RETURN_TYPE ( OBJECT_TYPE*, INPUT_TYPE )> fn )
    : m_ObjectPTR ( obj )
    , m_Function ( fn )
  {
  }

  //! \brief Operator.
  //!	returns : obj->fn( input )
  RETURN_TYPE operator ()  ( INPUT_TYPE input ) const
  {
    return m_Function( m_ObjectPTR, input );
  }

};

/**
 * \brief Creates a delegate for a method with non void parameters.
 * \param	obj		The object.
 * \param	FN		The functions.
 * \return  The delegate as std::function.
 */
template <typename OBJECT_TYPE, typename RETURN_TYPE, typename INPUT_TYPE>
static std::function<RETURN_TYPE( INPUT_TYPE )> CreateDelegate(
  OBJECT_TYPE* obj,
  RETURN_TYPE ( OBJECT_TYPE::* FN )( INPUT_TYPE ))
{
  return DelegateInOut<OBJECT_TYPE, INPUT_TYPE, RETURN_TYPE>( obj, FN );
};

/*!
 * \author	Stefan Kazalski
 *
 * \brief	Delegate without parameters.
 *
 *   Can be created by StompedReality::CreateDelegate(obj,&OBJECT::FUNCTION)
 */
template <class OBJECT_TYPE, typename RETURN_TYPE>
class DLL_EXPORT DelegateOut
{
 private:

  std::function<RETURN_TYPE ( OBJECT_TYPE* )> m_Function;
  OBJECT_TYPE* m_ObjectPTR;

 public:

  //! \brief	Constructor.
  //! \param	obj		The Obejct.
  //! \param	fn
  DelegateOut ( OBJECT_TYPE* obj, std::function<RETURN_TYPE ( OBJECT_TYPE* )> fn )
    : m_ObjectPTR ( obj )
    , m_Function ( fn )
  {
  }

  //! \brief Operator.
  //!	returns : obj->fn()
  RETURN_TYPE operator ()  ( ) const
  {
    return m_Function( m_ObjectPTR );
  }

};

/**
 * \brief Creates a delegate for a method without parameters.
 * \param	obj		The object.
 * \param	FN		The functions.
 * \return  The delegate as std::function.
 */
template <typename OBJECT_TYPE, typename RETURN_TYPE>
static std::function<RETURN_TYPE ( )> CreateDelegate(
  OBJECT_TYPE* obj,
  RETURN_TYPE ( OBJECT_TYPE::* FN )( ) )
{
  return DelegateOut<OBJECT_TYPE, RETURN_TYPE>( obj, FN );
};

template <typename RETURN_TYPE>
class DLL_EXPORT DelegateValueGetter
{
 private:

  RETURN_TYPE* m_ValuePtr;

 public:

  //! \brief	Constructor.
  //! \param	obj		The object.
  //! \param	offset	The offset.
  DelegateValueGetter ( char* obj, int offset )
  {
    this->m_ValuePtr = (RETURN_TYPE*) (obj + offset);
  }

  //! \brief Operator.
  //!	returns : value
  RETURN_TYPE operator ()  ( ) const
  {
    return *(this->m_ValuePtr);
  }

};

template <typename TYPE>
class DLL_EXPORT DelegateValueSetter
{
 private:

  TYPE* m_ValuePtr;

 public:

  //! \brief	Constructor.
  //! \param	obj		The object.
  //! \param	offset	The offset.
  DelegateValueSetter ( char* obj, int offset )
  {
    this->m_ValuePtr = (TYPE*) (obj + offset);
  }

  //! \brief Operator.
  //!	sets value.
  void operator ()  ( TYPE in ) const
  {
    *(this->m_ValuePtr) = in;
  }

};

template <typename TYPE>
class DLL_EXPORT ValueDelegate
{
 private:

  TYPE* m_ValuePtr;

 public:

  ValueDelegate( void* ptr )
  {
    this->m_ValuePtr = (TYPE*) ptr;
  }

  TYPE* ValuePtr( )
  {
    return this->m_ValuePtr;
  }

  void Set( const TYPE value )
  {
    *m_ValuePtr = value;
  }

  const TYPE Get( )
  {
    return *m_ValuePtr;
  }

};

}

#endif // DELEGATE_H
